package aino;

import java.io.*;
import java.util.*;

public class CodeGen {
    int state;
    String fName;
    CodeLines codeLines;
    Stack[] stack;
    int stkptr = 0;
    String className;
    String funcName;
    String currentLabel;
    String[] instances;
    int instanceCnt = 0;
    boolean debug = false;
    PrintWriter out;

    final static int EQ = 0;
    final static int NE = 1;
    final static int GT = 2;
    final static int GE = 3;
    final static int LT = 4;
    final static int LE = 5;

    public CodeGen() {
	codeLines = new CodeLines();
	stack = new Stack[50];
    }

    public void compile(Method m) {
    }

    static int cmpToInt(String s) {
	if (s.equals("eq")) return EQ;
	if (s.equals("ne")) return NE;
	if (s.equals("gt")) return GT;
	if (s.equals("ge")) return GE;
	if (s.equals("lt")) return LT;
	if (s.equals("le")) return LE;
	return 0;
    }

    static int cmdToCmp(int cmd) {
	switch (cmd) {
	case 0xc6: //ifnull
	case 0xa5: //if_acmpeq
	case 0x9f: //if_icmpeq
	case 0x99: //ifeq
	    return EQ;
	case 0xc7: //ifnonnull
	case 0xa6: //if_acmpne
	case 0xa0: //if_icmpne
	case 0x9a: //ifne
	    return NE; 
	case 0xa1: //if_icmplt
	case 0x9b: //iflt
	    return LT; 
	case 0xa2: //if_icmpge
	case 0x9c: //ifge
	    return GE; 
	case 0xa3: //if_icmpgt
	case 0x9d: //ifgt
	    return GT; 
	case 0xa4: //if_icmple
	case 0x9e: //ifle
	    return LE; 
	}
	return 0;
    }

    static int complementCmp(int cmp) {
	switch (cmp) {
	case EQ: return EQ;
	case NE: return NE;
	case GT: return LT;
	case GE: return LE;
	case LT: return GT;
	case LE: return GE;
	}
	return 0;
    }

    void pushStack(Stack s) {
	stack[stkptr] = s;
	stkptr++;
	if (stkptr > 19) exit("Stack ptr overflow");
    }

    Stack popStack() {
	stkptr--;
	if (stkptr < 0) exit("Stack ptr underflow ");
	if (stack[stkptr] == null) exit("Stack has null ptr");
	return stack[stkptr];
    }

    Stack getStack() {
	return stack[stkptr-1];
    }

    int findFreeTmp() {
	boolean[] used = new boolean[10];
	for (int i = 0; i < 10; i++) used[i] = false;
	for (int i = 0; i < stkptr; i++) {
	    String s = stack[i].value;
	    int found;
	    if ((found = s.indexOf("_tmp+")) > 0) {
		used[s.charAt(found+5)-'0'] = true;
	    }
	    if (stack[i].size == 2) {
		s = stack[i].value2;
		if (s != null) {
		    if ((found = s.indexOf("_tmp+")) > 0) {
			used[s.charAt(found+5)-'0'] = true;
		    }
		}
	    }
	}
	for (int i = 0; i < 10; i++)
	    if (used[i] == false) return i;

	return -1;
    }

    void checkAcc(int dont) {
	for (int i = 0; i < stkptr-dont; i++) {
	    if (stack[i].type == Stack.ACC) {
		int freeTmp = findFreeTmp();
		if (freeTmp < 0) {
		    exit("No space for tmp vars ");
		}
		String addr = funcName+"_tmp+"+
		    Integer.toString(freeTmp);
		codeLines.addCode("movwf\t"+addr);
		stack[i].type = Stack.ADDR;
		stack[i].value = addr;

		if (stack[i].size == 2) {
		    int freeTmp2 = findFreeTmp();
		    if (freeTmp2 < 0) {
			exit("No space for tmp2 vars ");
		    }
		    String addr2 = funcName+"_tmp+"+
			Integer.toString(freeTmp2);
		    codeLines.addCode("movf\t_ACC,0");
		    codeLines.addCode("movwf\t"+addr2);
		    stack[i].value2 = addr2;
		}
		break;
	    }
	} 
    }

    void checkAcc() {
	checkAcc(0);
    }

    void loadAcc(Stack s) {
	if (s.type != Stack.ACC) checkAcc();

	switch (s.type) {
	case Stack.CONST:
	    int val = Integer.decode(s.value).intValue();
	    if (s.size == 2) {
		codeLines.addCode("movlw\t"+(val>>8));
		codeLines.addCode("movwf\t_ACC");
		codeLines.addCode("movlw\t"+(val&0xff));
	    } else {
		codeLines.addCode("movlw\t"+val);
	    }
	    break;
	case Stack.ADDR:
	case Stack.REF:
	    if (s.size == 2) {
		codeLines.addCode("movf\t"+s.value2+",0");
		codeLines.addCode("movwf\t_ACC");
		codeLines.addCode("movf\t"+s.value+",0");
	    } else {
		codeLines.addCode("movf\t"+s.value+",0");
	    }
	    break;
	case Stack.ACC:
	    break;
	case Stack.STRING:
	    System.out.println("\n*** Warning: String operation!");
	    break;
	default:
	    exit("Cannot load acc ");
	}
    }

    void loadAccLo(Stack s) {
	if (s.type != Stack.ACC) checkAcc();

	switch (s.type) {
	case Stack.CONST:
	    int val = Integer.decode(s.value).intValue();
	    codeLines.addCode("movlw\t"+(val&0xff));
	    break;
	case Stack.ADDR:
	    codeLines.addCode("movf\t"+s.value+",0");
	    break;
	case Stack.ACC:
	    break;
	case Stack.STRING:
	    System.out.println("\n*** Warning: String operation!");
	    break;
	default:
	    exit("Cannot load acc ");
	}
    }

    void loadAccHi(Stack s) {
	if (s.type != Stack.ACC) checkAcc();

	switch (s.type) {
	case Stack.CONST:
	    int val = Integer.decode(s.value).intValue();
	    codeLines.addCode("movlw\t"+(val>>8));
	    break;
	case Stack.ADDR:
	    codeLines.addCode("movf\t"+s.value2+",0");
	    break;
	case Stack.ACC:
	    codeLines.addCode("movf\t_ACC,0");
	    break;
	case Stack.STRING:
	    System.out.println("\n*** Warning: String operation!");
	    break;
	default:
	    exit("Cannot load acc ");
	}
    }

    void moveFromStack(int count, String fname) {
	int argsmoved = 0;
	checkAcc();
	for (int k = stkptr - count; k < stkptr; k++, argsmoved++) {
	    if (k < 0 || k >= 20) exit("moveFromStack fails, k="+k);
	    switch (stack[k].type) {
	    case Stack.CONST:
		if (stack[k].size == 2) {
		    codeLines.addCodeComment("movlw\t"+ 
					     (Integer.decode(stack[k].value).intValue()>>8), "const");
		    codeLines.addCode("movwf\t"+fname+"_loc+"+(argsmoved++));
		}
		codeLines.addCodeComment("movlw\t"+
					 (Integer.decode(stack[k].value).intValue()&0xff), "const");
		codeLines.addCode("movwf\t"+fname+"_loc+"+argsmoved);
		break;
	    case Stack.TMP:
		codeLines.addCode("movf\t"+funcName+"_tmp+"+stack[k].value+",0");
		codeLines.addCode("movwf\t"+fname+"_loc+"+argsmoved);
		break;
	    case Stack.REF:
		codeLines.addCode("movf\t"+funcName+"_loc+0,0");
		codeLines.addCode("movwf\t"+fname+"_loc+"+argsmoved);
		break;
	    case Stack.ADDR:
		if (stack[k].size == 2) {
		    codeLines.addCode("movf\t"+stack[k].value2+",0");
		    codeLines.addCode("movwf\t"+fname+"_loc+"+(argsmoved++));
		}
		codeLines.addCode("movf\t"+stack[k].value+",0");
		codeLines.addCode("movwf\t"+fname+"_loc+"+argsmoved);
		break;
	    case Stack.ACC:
		if (stack[k].size == 2) {
		    codeLines.addCode("movwf\t"+fname+"_loc+"+(argsmoved+1));
		    codeLines.addCode("movf\t_ACC,0");
		    codeLines.addCode("movwf\t"+fname+"_loc+"+(argsmoved++));
		} else {
		    codeLines.addCode("movwf\t"+fname+"_loc+"+argsmoved);
		}
		break;
	    default:
		System.out.println("\n*** Internal error: Unknown stack type!");
	    } // switch
	} // for
	stkptr -= count;
    }

    void printStack() {
	for (int i = stkptr-1; i >= 0; i--) {
	    Stack s = stack[i];
	    System.out.print(Integer.toString(i)+": ");
	    if (s.getTypeString() != null) {
		System.out.print(s.getTypeString()+" = ");
		if (s.value!=null) System.out.print(s.value);
		else System.out.print("(null)");
	    }
	    System.out.println();
	}
    }

    void printStack(String s) {
	System.out.println("\n"+s);
	printStack();
    }

    void exit(String msg) {
	codeLines.print(out);
	out.close();
	System.out.println("\n*** Internal error: "+msg+" at "+currentLabel+" stack ptr = "+Integer.toString(stkptr));
	printStack();
	Exception err = new Exception();
	err.printStackTrace();
	System.out.flush();
	System.exit(5);
    }

    void report() {
	System.out.println(currentLabel+": stack ptr = "+Integer.toString(stkptr));
	printStack();
	System.out.flush();
    }
}

